package com.ops.uitl.jdbc;

import com.ops.uitl.common.Common;
import org.apache.log4j.Logger;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import javax.transaction.UserTransaction;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class ConnectionManager {
	
	public static final String TRANSACTION_MODE_JDBC = "jdbc";
	public static final String TRANSACTION_MODE_XA = "xa";
	
	private final Logger log = Logger.getLogger(this.getClass());
	
	private static ConnectionManager instance;
	
	/**
	 * get instance
	 * @return ConnectionManager
	 * @throws Exception
	 */
	public static ConnectionManager getInstance() throws Exception {
		if (instance == null) {
			instance = new ConnectionManager();
		}
		return instance;
	}
	
	/**
	 * get default name
	 * @param eparchyId
	 * @return String
	 * @throws Exception
	 */
	public String getDefaultName(String eparchyId) throws Exception {
		return getDefaultName(eparchyId, "eparchy");
	}
	
	/**
	 * get default name
	 * @param eparchyId
	 * @param database
	 * @return String
	 * @throws Exception
	 */
	public String getDefaultName(String eparchyId, String database) throws Exception {
		Common common = Common.getInstance();
		
		String defaultConn = common.getProperty("database/default");
		if (database.equals(defaultConn)) {
			String eparchyPath = "database/" + database + "/";
			String eparchyConn = common.getProperty(eparchyPath + (eparchyId == null ? "default" : "conn" + eparchyId));
			if (eparchyConn != null) return eparchyConn;
			return common.getProperty(eparchyPath + "default");
		}
		
		return defaultConn;
	}
	
	/**
	 * get default name by database
	 * @param eparchyId
	 * @param database
	 * @return String
	 * @throws Exception
	 */
	public String getDefaultNameByDatabase(String eparchyId, String database) throws Exception {
		Common common = Common.getInstance();
		
		String eparchyPath = "database/" + database + "/";
		String eparchyConn = common.getProperty(eparchyPath + (eparchyId == null ? "default" : "conn" + eparchyId));
		if (eparchyConn != null) return eparchyConn;
		return common.getProperty(eparchyPath + "default");
	}
	
	/**
	 * get names
	 * @param eparchyIds
	 * @return String[]
	 * @throws Exception
	 */
	public String[] getNames(String[] eparchyIds) throws Exception {
		return getNames(eparchyIds, "eparchy");
	}
	
	/**
	 * get names
	 * @param eparchyIds
	 * @param database
	 * @return String[]
	 * @throws Exception
	 */
	public String[] getNames(String[] eparchyIds, String database) throws Exception {
		Common common = Common.getInstance();
		String defval = common.getProperty("database/" + database + "/default");
		
		List list = new ArrayList();
		for (int i=0; i<eparchyIds.length; i++) {
			String name = common.getProperty("database/" + database + "/conn" + eparchyIds[i], defval);
			if (!list.contains(name)) list.add(name);
		}
		return (String[]) list.toArray(new String[0]);
	}
	
	/**
	 * get all names
	 * @return String[]
	 * @throws Exception
	 */
	public String[] getAllNames() throws Exception {
		return getAllNames("eparchy");
	}
	
	/**
	 * get all names
	 * @param database
	 * @return String[]
	 * @throws Exception
	 */
	public String[] getAllNames(String database) throws Exception {
		Common common = Common.getInstance();
		List list = new ArrayList();
		
		String[] arrays = (String[]) common.getProperties("database/" + database).values().toArray(new String[0]);
		Arrays.sort(arrays);
		for (int i=0; i<arrays.length; i++) {
			if (!list.contains(arrays[i])) list.add(arrays[i]);
		}
		
		return (String[]) list.toArray(new String[0]);
	}
	
	/**
	 * get connection
	 * @param name
	 * @return Connection
	 * @throws Exception
	 */
	public Connection getConnection(String name) throws Exception {
		return getConnection(name, TRANSACTION_MODE_JDBC);
	}
	
	/**
	 * get connection
	 * @param name
	 * @param transactionMode
	 * @return Connection
	 * @throws Exception
	 */
	public Connection getConnection(String name, String transactionMode) throws Exception {
		if (log.isDebugEnabled()) log.debug("get " + transactionMode + " db connection..." + name);
		
		Common common = Common.getInstance();
		
		String path = "database/" + name;
		String type = common.getProperty(path + "/type");
		if ("jndi".equals(type)) {
			long beginTime = System.currentTimeMillis();
			String jndi = common.getProperty(path + "/jndi");
			if (TRANSACTION_MODE_XA.equals(transactionMode)) jndi += TRANSACTION_MODE_XA;
			
			Context ctx = new InitialContext();
			DataSource ds = (DataSource) ctx.lookup(jndi);
			ctx.close();
			Connection conn = ds.getConnection();
			if (log.isDebugEnabled()) log.debug("get " + name + " conn cost time: " + ((double) (System.currentTimeMillis() - beginTime) / (double) 1000) + "s");
			return conn;
		}
		if ("jdbc".equals(type)) {
			long beginTime = System.currentTimeMillis();
			String driver = common.getProperty(path + "/driver");
			String url = common.getProperty(path + "/url");
			String user = common.getProperty(path + "/user");
			String passwd = common.getProperty(path + "/passwd");
			
			Class.forName(driver);
			Connection conn = DriverManager.getConnection(url, user, passwd);
			if (log.isDebugEnabled()) log.debug("get " + name + " conn cost time: " + ((double) (System.currentTimeMillis() - beginTime) / (double) 1000) + "s");
			return conn;
		}
		
		return null;
	}
    
	/**
	 * close connection
	 * @throws Exception
	 */
    public void closeConnection(Connection conn) throws Exception {
    	if (!conn.isClosed()) conn.close();
    	conn = null;
    }
    
	/**
	 * get user transaction
	 * @return UserTransaction
	 * @throws Exception
	 */
	public UserTransaction getUserTransaction() throws Exception {
		Context ctx = new InitialContext();
		return (UserTransaction) ctx.lookup("javax.transaction.UserTransaction");
	}
    
}